package com.jinke.api.modules.ums.task;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.jinke.api.common.api.CommonResult;
import com.jinke.api.common.util.CommonUtil;
import com.jinke.api.common.util.FileUtil;
import com.jinke.api.common.util.Global;
import com.jinke.api.modules.api.conf.ApiUrlsConfig;
import com.jinke.api.modules.api.controller.resp.ApiResult;
import com.jinke.api.modules.app.enums.OrderStatus;
import com.jinke.api.modules.app.enums.OrderTaskDetailEnums;
import com.jinke.api.modules.app.enums.OrderTaskEnums;
import com.jinke.api.modules.app.enums.OrderType;
import com.jinke.api.modules.app.model.Order;
import com.jinke.api.modules.app.model.OrderParcel;
import com.jinke.api.modules.app.model.OrderTask;
import com.jinke.api.modules.app.model.OrderTaskDetail;
import com.jinke.api.modules.app.response.TrackSummaryVO;
import com.jinke.api.modules.app.response.UserOrderSummaryVO;
import com.jinke.api.modules.app.service.*;
import com.jinke.api.modules.app.util.MathUtil;
import com.jinke.api.modules.app.util.OssUtil;
import com.jinke.api.modules.base.service.BaseShipService;
import com.jinke.api.modules.base.service.ShipFactory;
import com.jinke.api.modules.third.ezeeship.model.request.EstimateRateRequest;
import com.jinke.api.modules.third.ezeeship.model.response.CreateLabelResponse;
import com.jinke.api.modules.third.myus56.service.IMyus56Service;
import com.jinke.api.modules.third.myus56.service.Impl.Myus56ServiceImpl;
import com.jinke.api.modules.third.myus56.util.Myus56Utils;
import com.jinke.api.modules.third.sjgj.service.SjgjService;
import com.jinke.api.modules.third.trackApi.TrackApiUtils;
import com.jinke.api.modules.third.trackApi.model.response.TrackVO;
import com.jinke.api.modules.ums.model.CancelApiLog;
import com.jinke.api.modules.ums.model.Channel;
import com.jinke.api.modules.ums.model.ChannelCarrier;
import com.jinke.api.modules.ums.model.PreSucLog;
import com.jinke.api.modules.ums.service.*;
import com.jinke.api.modules.ums.vo.SimpleUser;
import com.jinke.api.modules.ums.vo.UserChannelVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * @author huang
 * @data 2024-04-26
 */
@Slf4j
@Component
@Profile({"prod"})
@RequestMapping("/task")
@RestController
public class OrderPlay {

    @Autowired
    private UmsAdminService adminService;
    @Resource
    private OssUtil ossUtil;
    @Resource
    private ApiUrlsConfig apiUrlsConfig;
    @Resource
    private IApiLogService iApiLogService;
    @Resource
    private OrderParcelService orderParcelService;
    @Autowired
    private SjgjService sjgjService;
    @Autowired
    private IOrderTaskService iOrderTaskService;
    @Autowired
    private IOrderTaskDetailService iOrderTaskDetailService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private ChannelService channelService;
    @Autowired
    private ChannelCarrierService channelCarrierService;
    @Autowired
    private ShipFactory shipFactory;
    @Resource
    private Executor createOrderThreadPool;
    @Resource
    private ICancelApiLogService iCancelApiLogService;
    @Resource
    private IMyus56Service iMyus56Service;
    @Resource
    private IPreSucLogService iPreSucLogService;
    @Autowired
    private Myus56ServiceImpl myus56Service;
    @Resource
    private Myus56Utils myus56Utils;

    //30秒一次
    public static final long THIRTY_SECONDS_IN_MILLISECONDS = 30 * 1000;
    //一分钟一次
    public static final long ONE_MINUTE_IN_MILLISECONDS = 60 * 1000;
    //三分钟一次
    public static final long THREE_MINUTE_IN_MILLISECONDS = 3 * 60 * 1000;
    //十分钟一次
    public static final long TEN_MINUTE_IN_MILLISECONDS = 10 * 60 * 1000;
    //30分钟一次
    private static final long HALF_HOUR_IN_MILLISECONDS = 30 * 60 * 1000;

    // 每天0点执行
    private static final String ZERO_CRON_EXPRESSION = "0 0 0 * * ?";
    public static final long TEST_TWO_SECONDS_IN_MILLISECONDS = 5 * 60 * 1000;

    //下午统计物流
    public static final String HALF_PAST_TEN = "0 50 14 * * ?";
    public static final String COTER_PAST_TEN = "0 0 15 * * ?";
    public static final String TEN_A_CLOCK = "0 50 14 * * ?";


    // 17TRACK 每次可提交 40 个物流单号
    private static final int BATCH_SIZE = 40;

    @Scheduled(fixedRate = TEN_MINUTE_IN_MILLISECONDS)
    public void cancelMYus56() {
        log.info("==================JOB定时取消批量下单中预报成功的环洋订单开始=============");
        //获取不超过三次待取消的
        List<CancelApiLog> list = iCancelApiLogService.getList();
        if (CollectionUtil.isNotEmpty(list)) {
            for (CancelApiLog cancelApiLog : list) {
                log.error(cancelApiLog.getOrderNumber() + "---------定时取消环洋订单开始-----------");

                //调接口取消
                String s = myus56Utils.cancleOrder(cancelApiLog.getOrderNo(), cancelApiLog.getOrderNumber());
                iApiLogService.saveApiLog(s, cancelApiLog.getOrderNo(), "Myus56-取消");

                Gson gson = new Gson();
                try {
                    ApiResult apiResult = gson.fromJson(s, ApiResult.class);
                    if (apiResult.getCode() == 200) {
                        cancelApiLog.setIsSucesss(1);
                    } else {
                        cancelApiLog.setIsSucesss(0);
                        cancelApiLog.setMsg(s);
                        cancelApiLog.setTryTime(cancelApiLog.getTryTime() + 1);
                    }
                    iCancelApiLogService.updateCancelApiLog(cancelApiLog);
                } catch (Exception e) {

                    log.error(cancelApiLog.getOrderNumber() + "环洋取消失败" + s + e.getMessage());
                }

            }
            //记录到新加的表中
            log.info(" ---------------定时取消环洋订单任务结束--------------------");
        } else {
            log.info("==================JOB没有需要定时取消的环洋订单=============");

        }
    }


    /**
     * 30秒批量下单
     */
    @Scheduled(fixedRate = THIRTY_SECONDS_IN_MILLISECONDS)
    @RequestMapping(value = "/xd", method = RequestMethod.GET)
    public void creatLabel() {
        log.info("==================JOB批量下单定时任务开始=============");
        //查询订单定时详情表中还未执行的数据
        List<OrderTaskDetail> orderTaskDetails = iOrderTaskDetailService.lambdaQuery()
                .isNull(OrderTaskDetail::getStatus)
                .eq(OrderTaskDetail::getOperate, 1)
                .list();
        if (CollectionUtil.isNotEmpty(orderTaskDetails)) {
            if (orderTaskDetails.size() >= 20) {
                orderTaskDetails = orderTaskDetails.subList(0, 20);
            }
            List<Integer> orderIdList = orderTaskDetails.stream().map(OrderTaskDetail::getOrderId).collect(Collectors.toList());
            log.info("=========批量下单执行数据==========orderIds,{}", JSON.toJSONString(orderIdList));
            //开启多线程
            for (OrderTaskDetail taskDetail : orderTaskDetails) {
                createOrderThreadPool.execute(() -> {
                    boolean isFlag = iOrderTaskDetailService.lambdaUpdate()
                            .eq(OrderTaskDetail::getOperate, 1)
                            .set(OrderTaskDetail::getOperate, 0)
                            .in(OrderTaskDetail::getId, taskDetail.getId())
                            .update();
                    if (isFlag) {
                        //查询订单，订单是否是下单中且未生成pdfurl
                        Order order = orderService.lambdaQuery()
                                .eq(Order::getId, taskDetail.getOrderId())
                                .isNull(Order::getPdfUrl)
                                .eq(Order::getStatus, OrderStatus.ORDER_PLAY)
                                .one();
                        if (ObjectUtils.isNotEmpty(order)) {
                            OrderTask orderTask = iOrderTaskService.getById(taskDetail.getOrderTaskId());
                            if (ObjectUtils.isNotEmpty(orderTask)) {
                                List<UserChannelVO> channels = channelService.getOnChannels(orderTask.getUserId(), null, taskDetail.getChannelId());
                                if (CollectionUtil.isNotEmpty(channels)) {
                                    UserChannelVO userChannelVO = channels.get(0);
                                    Channel channel = userChannelVO.getChannel();
                                    if (ObjectUtils.isNotEmpty(channel)) {
                                        ChannelCarrier channelCarrier = channelCarrierService.getOne(new LambdaQueryWrapper<ChannelCarrier>()
                                                .eq(ChannelCarrier::getId, channel.getChannelCarrierId()));
                                        //渠道不为空
                                        if (ObjectUtils.isNotEmpty(channelCarrier)) {
                                            EstimateRateRequest estimateRateRequest = orderService.getEstimateRateRequest(taskDetail.getOrderId());
                                            estimateRateRequest.setOrderType(OrderType.BAAPI);
                                            estimateRateRequest.setChannelId(taskDetail.getChannelId());
                                            orderService.fillParcel(taskDetail.getOrderId(), channel, estimateRateRequest);
                                            estimateRateRequest.setPlatformNo(CommonUtil.getOrderNo());
                                            estimateRateRequest.setCarrierCode(channelCarrier.getChannelCarrierCode());
                                            estimateRateRequest.setServiceCode(channel.getChannelCode());

                                            estimateRateRequest.setUserChannelVO(userChannelVO);
                                            MathUtil.fmtWeight2(estimateRateRequest);
                                            //获取对应的渠道服务
                                            BaseShipService baseShipService = shipFactory.getBaseShipService(channel);


                                            CommonResult commonResult;
                                            CommonUtil.handle(estimateRateRequest);
                                            if (baseShipService instanceof IMyus56Service) {
                                                //只到预报成功
                                                commonResult = iMyus56Service.createLabelForPage(estimateRateRequest, orderTask.getUserId());
                                            } else {

                                                commonResult = baseShipService.createLabel(estimateRateRequest, orderTask.getUserId());
                                            }
                                            //根据下单返回执行
                                            updateByResult(taskDetail, commonResult);


                                        }
                                    }
                                }
                            }
                        }
                    }
                });
            }
        } else {
            log.info("=============JOB无批量下单任务================");

        }
        log.info("=============JOB批量下单定时任务执行结束================");

    }

    private void updateByResult(OrderTaskDetail taskDetail, CommonResult commonResult) {
        log.error("更新任务祥情结果1" + JSONUtil.toJsonStr(commonResult));
        boolean update = iOrderTaskDetailService.lambdaUpdate()
                .set(OrderTaskDetail::getStatus, commonResult.isSucess() ? OrderTaskDetailEnums.SUCCESS : OrderTaskDetailEnums.FAILURE)
                .set(!commonResult.isSucess(), OrderTaskDetail::getMessage, commonResult.getMessage())
                .eq(OrderTaskDetail::getId, taskDetail.getId())
                .update();
        log.error("更新任务祥情结果" + update + JSONUtil.toJsonStr(commonResult));
        iOrderTaskService.lambdaUpdate().setSql(commonResult.isSucess(), "success = success + 1")
                .setSql(!commonResult.isSucess(), "failure = failure + 1")
                //进行中
                .set(OrderTask::getStatus, OrderTaskEnums.CNDER_WAY)
                .set(OrderTask::getUpdateBy, "admin")
                .eq(OrderTask::getId, taskDetail.getOrderTaskId())
                .update();


        //更改订单状态
        orderService.lambdaUpdate()
                .eq(Order::getId, taskDetail.getOrderId())
                .set(!commonResult.isSucess(), Order::getStatus, OrderStatus.DRAFT)
                .set(!commonResult.isSucess(), Order::getErrMsg, commonResult.getMessage())
                .setSql(commonResult.isSucess(), "err_msg = null")
                .update();
    }

    /**
     * 每分钟查看环洋订单是否从预报成功变成成功成功进行真正下单逻辑
     */
  @Scheduled(fixedRate = ONE_MINUTE_IN_MILLISECONDS)
    public void askSuccess() {
        log.info("==================JOB每分钟查看环洋订单是否从预报成功变成真成功=============");
        //查询订单，订单预报成功的
        List<PreSucLog> list = iPreSucLogService.getList();
        if (CollectionUtil.isNotEmpty(list)) {
            for (PreSucLog preSucLog : list) {
                //查询 订单
                Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
                        .eq(Order::getId, preSucLog.getOrderId()
                        )
                );
                if (order.getChannelId() == null) {

                    orderService.update(new LambdaUpdateWrapper<Order>().eq(Order::getId, order.getId()).set(Order::getErrMsg,
                            "批量下单未成功，请手动单个重新下单"));


                    continue;

                }
                UserChannelVO userChannelVO = null;
                Channel channel = null;
                ChannelCarrier channelCarrier = null;
                try {

                    userChannelVO = channelService.getChannelById(order.getUserId(), order.getChannelId());
                    channel = userChannelVO.getChannel();
                    channelCarrier = channelCarrierService.getOne(new LambdaQueryWrapper<ChannelCarrier>()
                            .eq(ChannelCarrier::getId, channel.getChannelCarrierId()));
                } catch (Exception e) {

                }


                EstimateRateRequest estimateRateRequest = orderService.getEstimateRateRequest(preSucLog.getOrderId());
                estimateRateRequest.setChannelId(preSucLog.getChannelId());


                orderService.fillParcel(preSucLog.getOrderId(), channel, estimateRateRequest);
                estimateRateRequest.setPlatformNo(CommonUtil.getOrderNo());
                estimateRateRequest.setCarrierCode(channelCarrier.getChannelCarrierCode());
                estimateRateRequest.setServiceCode(channel.getChannelCode());

                estimateRateRequest.setUserChannelVO(userChannelVO);
                MathUtil.fmtWeight2(estimateRateRequest);
                //看预成功是否转成成功
                CommonResult commonResult = myus56Service.ifPreSuccessToTrue(preSucLog.getRequestId(),
                        estimateRateRequest, preSucLog.getUserId(), order.getRate(), preSucLog.getOrderNumber(), order.getPlatformNo());


                //根据下单返回执行
                try {
                    if (commonResult.getCode() == 200) {
                        preSucLog.setIsSucesss(1);
                    } else {
                        //失败 了判断失败次数
                        preSucLog.setMsg(commonResult.getMessage());
                        log.info("更新预报成功表{},当前次数为{}", preSucLog.getOrderNumber(), preSucLog.getTryTime());
                        preSucLog.setTryTime(preSucLog.getTryTime() + 1);
                        if (preSucLog.getTryTime() == 6) {
                            //回退到草稿状态
                            orderService.lambdaUpdate()
                                    .eq(Order::getId, preSucLog.getOrderId())
                                    .set(Order::getStatus, OrderStatus.DRAFT)
                                    .set(Order::getErrMsg, "批量下单失败，请单个手动下单")
                                    .update();
                            //草稿取消
                            String s = myus56Utils.cancleOrder(preSucLog.getOrderNumber(), preSucLog.getOrderNumber());
                            iApiLogService.saveApiLog(s, preSucLog.getOrderNumber(), "Myus56-取消");

                            Gson gson = new Gson();
                            ApiResult apiResult = gson.fromJson(s, ApiResult.class);
                            if (apiResult.getCode() == 200) {
                                log.info("更新预报成功表{},{}次不成功三方取消成功", preSucLog.getOrderNumber(), preSucLog.getTryTime());
                            } else {
                                log.info("更新预报成功表{},{}次不成功三方取消失败,加入定时取消表", preSucLog.getOrderNumber(), preSucLog.getTryTime());
                                iCancelApiLogService.saveCancelApiLog(preSucLog.getOrderNumber(), preSucLog.getOrderId() + "", preSucLog.getUserId());
                            }

                        }
                    }
                    log.info("更新预报成功表{},本次次数为{}", preSucLog.getOrderNumber(), preSucLog.getTryTime());
                    iPreSucLogService.updateCancelApiLog(preSucLog);
                } catch (Exception e) {

                    log.error(preSucLog.getOrderNumber() + "更新预报成功表失败" + e.getMessage());
                }

            }
        } else {
            log.info("=================JOB环洋订单批量下单预报转成功无可执行内容==================");
        }
    }


    /**
     * 统计这一批次的订单成功多少失败多少
     */
    @Scheduled(fixedRate = THREE_MINUTE_IN_MILLISECONDS)
    public void autocomplete() {
        log.info("=================JOB统计这一批次的订单成功多少失败多少==================");

        List<OrderTask> orderTasks = iOrderTaskService.lambdaQuery()
                .eq(OrderTask::getStatus, OrderTaskEnums.CNDER_WAY)
                .list();
        if (CollectionUtil.isNotEmpty(orderTasks)) {
            for (OrderTask orderTask : orderTasks) {
                List<OrderTaskDetail> orderTaskDetails = iOrderTaskDetailService.lambdaQuery()
                        .in(OrderTaskDetail::getOrderTaskId, orderTask.getId())
                        .list();
                if (CollectionUtil.isNotEmpty(orderTaskDetails)) {
                    boolean isFlag = true;
                    for (OrderTaskDetail detail : orderTaskDetails) {
                        if (ObjectUtils.isEmpty(detail.getStatus())) {
                            isFlag = false;
                            break;
                        }
                    }
                    if (isFlag) {
                        iOrderTaskService.lambdaUpdate()
                                .set(OrderTask::getStatus, OrderTaskEnums.FINISH)
                                .set(OrderTask::getUpdateBy, "admin")
                                .eq(OrderTask::getId, orderTask.getId())
                                .update();
                    }
                }
            }
            log.info("=================JOB统计这一批次的订单成功多少失败多少统计结束{}条==================", orderTasks);

        } else {
            log.info("=================JOB统计这一批次的订单成功多少失败多少==================");

        }
    }


    /**
     * 创建时间超过10分钟，还在”下单中“，将订单状态改回草稿
     */
    @Scheduled(fixedRate = TEN_MINUTE_IN_MILLISECONDS)
    public void backToDraft() {
        log.info("=================JOB创建时间超过10分钟的订单还在下单中退加草稿开始==================");

        //查询订单中处于下单中的订单
        List<Order> orders = orderService.lambdaQuery()
                .eq(Order::getStatus, OrderStatus.ORDER_PLAY)
                .list();
        if (CollectionUtil.isNotEmpty(orders)) {
            //执行过且创建时间超过10分钟，将订单状态改回草稿
            LocalDateTime time = LocalDateTime.now().minusMinutes(10);
            //查看定时任务详情是否执行，
            List<OrderTaskDetail> detailList = iOrderTaskDetailService.lambdaQuery()
                    .in(OrderTaskDetail::getOrderId, orders.stream().map(Order::getId).collect(Collectors.toList()))
                    .eq(OrderTaskDetail::getOperate, 0)
                    .le(OrderTaskDetail::getCreateTime, time)
                    .list();
            if (CollectionUtil.isNotEmpty(detailList)) {
                log.info("-------失败订单id------，{}", JSON.toJSONString(detailList.stream().map(OrderTaskDetail::getOrderId)));
                for (OrderTaskDetail taskDetail : detailList) {
                    Order order = orderService.getById(taskDetail.getOrderId());
                    if (ObjectUtils.isNotEmpty(order)) {
                        order.setStatus(OrderStatus.DRAFT.getValue());
                        if (StringUtils.isEmpty(order.getErrMsg())) {
                            order.setErrMsg("下单失败，请稍后再试");
                        }
                        try {

                            Channel channel = channelService.getOne(new LambdaQueryWrapper<Channel>().eq(Channel::getId, order.getChannelId()));

                            BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
                            baseShipService.cancelLabel(order.getObjectId(),order.getOrderNo());
                            log.error("定时取消三方成功" + order.getObjectId());
                        } catch (Exception e) {
                            log.error(e.getMessage() + "定时取消三方失败");
                        }

                        orderService.updateById(order);
                    }
                }
            }
            log.info("--------------------JOB退回草稿定时任务结束{}条-----------------------------------", orders.size());
        } else {
            log.info("=================JOB没有十分钟还没成功要退回草稿的==================");

        }
    }


    /**
     * 每30分钟取延迟单90 的面单
     */
    @Scheduled(fixedRate = HALF_HOUR_IN_MILLISECONDS)
    public String getDelayMD() {
        return slowTimeOrder(null);
    }

    /**
     * 接口
     *
     * @param userId
     * @return
     */
    @RequestMapping(value = "/yanchidan", method = RequestMethod.GET)
    public String getDelayMD(Integer userId) {
        return slowTimeOrder(userId);
    }


    @NotNull
    private String slowTimeOrder(Integer userId) {
        log.error("==================延迟面单定时任务开始=============");
        List<Integer> channelIdList = new ArrayList<>();
        //9001
        channelIdList.add(10310);
        channelIdList.add(10311);
        channelIdList.add(10312);
        channelIdList.add(10313);
        //9005
        channelIdList.add(10314);
        channelIdList.add(10315);


        LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
        //查订单id 订单号和平台号
        queryWrapper.select(Order::getId, Order::getOrderNo,
                Order::getPlatformNo,
                Order::getCreatedAt
        );
        queryWrapper.in(Order::getChannelId, channelIdList);
        //指定先更新某着急的客户的
        queryWrapper.eq(userId != null, Order::getUserId, userId);
        queryWrapper.and(wp -> {
            wp.in(Order::getStatus, OrderStatus.IN_TRANSIT, OrderStatus.RECENT, OrderStatus.DELIVERED);
        });
        //面单为空
        queryWrapper.isNull(Order::getPdfUrl);

//        queryWrapper.lt(Order::getCreatedAt,"2024-08-02 16:00:00");
//        queryWrapper.gt(Order::getCreatedAt,"2024-08-01 23:17:00");
//         queryWrapper.eq(  Order::getBatchNo, "8.1-（96）上午 台州_20240801122925");

        //查询延迟订单
        List<Order> orders = orderService.list(queryWrapper);

        if (CollectionUtil.isNotEmpty(orders)) {
            // 昨天晚上 6:30 的时间点
            LocalDateTime yesterdayAt630 = LocalDateTime.of(2024, 7, 29, 18, 30);

            // 按时间分组
            Map<Boolean, List<Order>> groupedOrders = orders.stream()
                    .collect(Collectors.partitioningBy(order -> order.getCreatedAt().isBefore(yesterdayAt630)));

            // 打印两组订单
            List<Order> beforeGroup = groupedOrders.get(true);
            List<Order> afterGroup = groupedOrders.get(false);


            //三方那边存的是平台号
            List<String> platformIds = beforeGroup.stream().map(Order::getPlatformNo).collect(Collectors.toList());
            Map<String, Integer> platformNo2orderIdMap = beforeGroup.stream().collect(Collectors.toMap(Order::getPlatformNo, Order::getId));
            //指定时间后 系统使用订单号进行传递 两边统一
            List<String> orderNoList = afterGroup.stream().map(Order::getOrderNo).collect(Collectors.toList());
            Map<String, Integer> orderNo2orderIdMap = afterGroup.stream().collect(Collectors.toMap(Order::getOrderNo, Order::getId));

            int batchSize = 40;
            if (CollectionUtil.isNotEmpty(beforeGroup)) {
                log.error("==================延迟面单29号6点半前=============" + orders.size() + "条中待更新物流条数-" + beforeGroup.size());

                for (int i = 0; i < platformIds.size(); i += batchSize) {

                    List<String> batchPlatformIds = platformIds.subList(i, Math.min(platformIds.size(), i + batchSize));

                    sjgjService.getLabelAndUpdate(platformNo2orderIdMap, batchPlatformIds);

                }
                log.error("=============延迟面单定时任务执行结束================");
            }
            //7-29晚上后用的订单号
            if (CollectionUtil.isNotEmpty(afterGroup)) {
                log.error("==================延迟面单29号6点半后=============" + orders.size() + "条中待更新物流条数-" + afterGroup.size());

                for (int i = 0; i < orderNoList.size(); i += batchSize) {

                    List<String> batchPlatformIds = orderNoList.subList(i, Math.min(orderNoList.size(), i + batchSize));


                    getLabelAndUpdate(orderNo2orderIdMap, batchPlatformIds);

                }
                log.error("=============延迟面单定时任务执行结束================");
            }
        }
        return "延迟面单定时任务结束" + orders.size();
    }

    /**
     * 获取面单并更新
     *
     * @param platformNo2orderIdMap
     * @param batchPlatformIds
     */
    private void getLabelAndUpdate(Map<String, Integer> platformNo2orderIdMap, List<String> batchPlatformIds) {
        //开启多线程
        createOrderThreadPool.execute(() -> {
            //走接口获取面单信息
            CommonResult label = sjgjService.getLabelfoys(batchPlatformIds);
            List<CreateLabelResponse.Data> reList = (List<CreateLabelResponse.Data>) label.getData();
            for (CreateLabelResponse.Data data : reList) {

                List<String> trackingNumber = data.getTrackingNumber();
                List<String> trackingUrl = data.getTrackingUrl();
                List<String> labelUrls = data.getLabelUrl();

                if (CollectionUtil.isNotEmpty(labelUrls)) {
                    //对应的订单号
                    Integer orderId = platformNo2orderIdMap.get(data.getOrderid());
                    //查询包裹
                    List<OrderParcel> parcels = orderParcelService.lambdaQuery()
                            .eq(OrderParcel::getOrderId, orderId)
                            .isNull(OrderParcel::getDeletedAt)
                            .list();
                    String mainPdfUrl = labelUrls.get(0);
                    for (int j = 0; j < parcels.size(); j++) {
                        OrderParcel orderParcel = parcels.get(j);
                        if (trackingNumber != null) {
                            orderParcel.setTrackingNumber(trackingNumber.get(j));
                        }
                        if (trackingUrl != null) {
                            orderParcel.setTrackingUrl(trackingUrl.get(j));
                        }
                        String tmpName = CommonUtil.getOrderNo();
                        try {
                            String pdf = ossUtil.uploadforYCD("pdf", tmpName + ".pdf", labelUrls.get(j));
                            if (StringUtils.isBlank(pdf)) {
                                //上传失败
                                orderParcel.setLabelUrl(mainPdfUrl);

                            } else {
                                //上传成功
                                String newLabelUrl = apiUrlsConfig.getDomain() + "/api/label/printLabel?objectId=" + tmpName;
                                if (j == 0) {
                                    //取第一个为新面单
                                    mainPdfUrl = newLabelUrl;
                                }
                                orderParcel.setLabelUrl(newLabelUrl);
                            }
                        } catch (Exception e) {
                            orderParcel.setLabelUrl(mainPdfUrl);
                            log.error("延迟单面单上传出错orderId-" + orderId);
                        }

                    }
                    //更新包裹
                    orderParcelService.updateBatchById(parcels);

                    LambdaUpdateWrapper<Order> updateWrapper = new LambdaUpdateWrapper<Order>()
                            .eq(Order::getId, orderId)
                            .set(Order::getPdfUrl, mainPdfUrl);
                    //更新订单pdfurl
                    orderService.update(updateWrapper);
                    log.error("延迟单更新成功" + data);
                } else {
                    log.error("延迟单暂时没获取到" + data);
                }
            }


        });
    }




    /**
     * 每天10点 统计昨天，前三天，一周的物流轨迹，再发邮件和站内信
     */
    @RequestMapping(value = "/1day", method = RequestMethod.GET)
    public void oneDaySummary() {
        log.info("==================JOB统计轨迹昨天开始=============");
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime now9 = now.withHour(9).withMinute(42).withSecond(0).withNano(0);
        LocalDateTime yesterdayD = now.minusDays(1).withHour(9).withMinute(42).withSecond(0).withNano(0);

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String today = now9.format(formatter);
        String yesterday = yesterdayD.format(formatter);

        //得到统计信息
        List<TrackSummaryVO> yesterdaySummary = infoText(today, yesterday);
        log.info("==================JOB统计轨迹昨天=============");
        adminService.checkSummaryThenSendMSG(today, yesterday, yesterdaySummary);


        log.info("==================JOB统计轨迹昨天结束=============");

    }

    @RequestMapping(value = "/3day", method = RequestMethod.GET)
    public void threeDaySummary() {
        log.info("==================JOB统计轨迹三天开始=============");
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime now9 = now.withHour(9).withMinute(42).withSecond(0).withNano(0);

        LocalDateTime threeDaysAgoD = now.minusDays(3);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String today = now9.format(formatter);
        String threeDaysAgo = threeDaysAgoD.format(formatter);

        //得到统计信息
        List<TrackSummaryVO> threeDaysAgoSummary = infoText(today, threeDaysAgo);
        log.info("==================JOB统计轨迹三天=============");
        adminService.checkSummaryThenSendMSG(today, threeDaysAgo, threeDaysAgoSummary);

        log.info("==================JOB统计轨迹三天结束=============");

    }


    /**
     * 7天
     */
    @Scheduled(cron = HALF_PAST_TEN)
    public void oneWeekSummary() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime now9 = now.withHour(9).withMinute(42).withSecond(0).withNano(0);
        LocalDateTime oneWeekAgoD = now.minusDays(7).withMinute(42).withSecond(0).withNano(0);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String endDay = now9.format(formatter);
        String startDay = oneWeekAgoD.format(formatter);


        orderService.summaryNoLogister(null,startDay,endDay);

    }




    /**
     * 每天9：42 会注册头一天生成面单的订单的物流
     * 每天10点查询头一天的9：42到今天9：42的物流号没轨迹的订单号+物流号 发站内信admin
     */
    private List<TrackSummaryVO> infoText(String today, String yesterday) {
        List<TrackSummaryVO> result = new ArrayList<>();

        //没有物流信息的单号
        List<String> noinfoTrackingNumbers = Lists.newArrayList();
        // 查询时间范围内待运输的
        List<Order> orders = Optional.ofNullable(
                orderService.lambdaQuery()
                        //待运输
                        .in(Order::getStatus, OrderStatus.RECENT)
                        .lt(Order::getCreateTime, today)
                        .gt(Order::getCreateTime, yesterday)
                        //.eq(Order::getUserId, 303)

                        .select(Order::getId, Order::getChannelId,
                                Order::getObjectId,
                                Order::getUserId,
                                Order::getCreateTime)
                        .orderByDesc(Order::getCreateTime)
                        .list()).orElse(Lists.newArrayList());

//        Map<Integer, Order> orderMap = orders.stream().collect(Collectors.toMap(Order::getId, Function.identity()));
        Map<Integer, List<Order>> userOrderMap = orders.stream().collect(Collectors.groupingBy(Order::getUserId));

        //只查近2天的，因为N多天前的订阅后会返回状态，不必再查是否在运输中
        List<Integer> orderIds = orders.stream().map(Order::getId).collect(Collectors.toList());

        if (CollectionUtil.isNotEmpty(orderIds)) {
            // 查询其中物流号无轨迹的
            List<OrderParcel> orderParcels = Optional.ofNullable(
                    orderParcelService.lambdaQuery()
                            .in(OrderParcel::getOrderId, orderIds)
                            .isNotNull(OrderParcel::getTrackingNumber)
                            .isNull(OrderParcel::getStatus)
                            .select(OrderParcel::getOrderId,
                                    OrderParcel::getId
                                    , OrderParcel::getTrackingNumber).list()
            ).orElse(Lists.newArrayList());


            // 包裹订单id
            List<String> trackingNumbers = orderParcels.stream().map(OrderParcel::getTrackingNumber).collect(Collectors.toList());

            //只查近2天的，因为N多天前的订阅后会返回状态，不必再查是否在运输中

            Map<Integer, List<OrderParcel>> parcelMap = orderParcels.stream().collect(Collectors.groupingBy(OrderParcel::getOrderId));

            // 确定列表的总大小
            int totalSize = trackingNumbers.size();

            if (CollectionUtil.isNotEmpty(trackingNumbers)) {
                // 分批次处理数据
                for (int i = 0; i < trackingNumbers.size(); i += BATCH_SIZE) {
                    List<String> batchData = trackingNumbers.subList(i, Math.min(trackingNumbers.size(), i + BATCH_SIZE));
                    // 走接口转json时给个无数据的json占位符 转换不会报错
                    String trackInfo = Optional.ofNullable(TrackApiUtils.getTrackinfo(batchData)).orElse("{}");
                    // 在track17的接口返回中一直没内容 则判断为没用
                    JSONObject jsonBody = JSONObject.parseObject(trackInfo);
                    Integer code = jsonBody.getInteger("code");

                    if (ObjectUtil.isNotNull(code)) {
                        if (code == 0) {
                            JSONObject data = Optional.ofNullable(jsonBody.getJSONObject("data")).orElse(new JSONObject());
                            JSONArray accepted = Optional.ofNullable(data.getJSONArray("accepted")).orElse(new JSONArray());
                            JSONArray rejected = Optional.ofNullable(data.getJSONArray("rejected")).orElse(new JSONArray());

                            // 有物流不做处理
                            for (int j = 0; j < accepted.size(); j++) {
                                JSONObject item = accepted.getJSONObject(j);
                                String number = item.getString("number");
                                JSONObject track_info = item.getJSONObject("track_info");
                                JSONObject latest_status = track_info.getJSONObject("latest_status");
                                String status = latest_status.getString("status");
                                if (status.equals("NotFound")) {
                                    noinfoTrackingNumbers.add(number);
                                }
                            }

                            if (!rejected.isEmpty()) {
                                // needCancelTrackingNumbers.add(number); todo才注册的也要先加到没物流的
                                log.info("trackinfo可能没注册的" + rejected);

                                TrackApiUtils.registerByResponds(rejected);
                            }


                        }
                    } else {
                        noinfoTrackingNumbers.addAll(batchData);
                        if (code != null && code.equals("18019909")) {
                            log.error("暂时没有跟踪信息。");
                        }
                        log.error("JOB无物流信息自动取消接口返回code" + code);
                    }
                }

            }


            TrackSummaryVO trackSummaryVO;
            for (Map.Entry<Integer, List<Order>> integerListEntry : userOrderMap.entrySet()) {

                Integer key = integerListEntry.getKey();
                List<Order> value = integerListEntry.getValue();

                int noinfonum = 0;
                List<String> noinfoList = new ArrayList<>();
                for (Order order : value) {

                    Integer orderId = order.getId();
                    List<OrderParcel> orderParcel = parcelMap.get(orderId);
                    if (orderParcel == null) {
                        log.error(orderId + "订单在运输中却没有运单号");
                        continue;
                    }
                    for (OrderParcel parcel : orderParcel) {
                        String trackingNumber = parcel.getTrackingNumber();

                        if (noinfoTrackingNumbers.contains(trackingNumber)) {
                            noinfonum++;
                            noinfoList.add(trackingNumber);
                        }
                    }
                }

                if (noinfonum != 0) {
                    trackSummaryVO = new TrackSummaryVO();
                    trackSummaryVO.setUserId(key);
                    trackSummaryVO.setTotal(value.size());
                    trackSummaryVO.setNoinfoNum(noinfonum);
                    trackSummaryVO.setNoinfoList(noinfoList);
                    result.add(trackSummaryVO);
                }

            }

        }

        return result;


    }


    /**
     * 每天凌晨0点执行半个月没动静的取消
     * usps不能自动取消
     * ups和fedex可以
     */
    @Scheduled(cron = ZERO_CRON_EXPRESSION)
    public void autoCancelOldOrder() {
        log.info("==================JOB半个月没动静运单的取消开始=============");
        //查询一个月未用
        LocalDate endDay = LocalDate.now().minusDays(30);
        //超2月三方一般不让取消了
        LocalDate twoMonthAgo = LocalDate.now().minusDays(60);
        // 查询七天前的已生成面单的订单
        // 包括已下单、近期、运输中和预定成功的订单状态
        List<Order> orders = Optional.ofNullable(
                orderService.lambdaQuery()
                        //待运输
                        .in(Order::getStatus, OrderStatus.RECENT)
                        .lt(Order::getCreateTime, endDay)
                        .gt(Order::getCreateTime, twoMonthAgo)
                        .select(Order::getId, Order::getChannelId, Order::getObjectId, Order::getOrderNo, Order::getCreateTime)
                        .orderByDesc(Order::getCreateTime)
                        .list()).orElse(Lists.newArrayList());

        Map<Integer, Order> orderMap = orders.stream().collect(Collectors.toMap(Order::getId, Function.identity()));
        List<Integer> orderIds = orders.stream().map(Order::getId).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(orderIds)) {

            // 查询30天前的运单 中 无轨迹的
            List<OrderParcel> orderParcels = Optional.ofNullable(
                    orderParcelService.lambdaQuery()
                            .in(OrderParcel::getOrderId, orderIds)
                            .isNotNull(OrderParcel::getTrackingNumber)
                            .isNull(OrderParcel::getStatus)
                            .select(OrderParcel::getOrderId, OrderParcel::getTrackingNumber).list()
            ).orElse(Lists.newArrayList());


            // 包裹订单id
            List<String> trackingNumbers = orderParcels.stream().map(OrderParcel::getTrackingNumber).collect(Collectors.toList());
            Map<String, Set<Integer>> trackingNumOrderIdMap = orderParcels.stream()
                    .collect(Collectors.groupingBy(OrderParcel::getTrackingNumber,
                            Collectors.mapping(OrderParcel::getOrderId, Collectors.toSet())));

            // 确定列表的总大小
            int totalSize = trackingNumbers.size();

            if (CollectionUtil.isNotEmpty(trackingNumbers)) {
                // 计算需要处理的批次数量
                int batches = (totalSize + BATCH_SIZE - 1) / BATCH_SIZE;
                // 分批次处理数据
                for (int i = 0; i < batches; i++) {
                    // 计算当前批次的起始索引和结束索引
                    int start = i * BATCH_SIZE;
                    int end = Math.min(start + BATCH_SIZE, totalSize);
                    // 获取当前批次的数据
                    List<String> batchData = trackingNumbers.subList(start, end);
                    // 走接口
                    // 转json时给个无数据的json占位符 转换不会报错
                    String trackInfo = Optional.ofNullable(TrackApiUtils.getTrackinfo(batchData)).orElse("{}");
                    // 在track17的接口返回中一直没内容 则判断为没用
                    JSONObject jsonBody = JSONObject.parseObject(trackInfo);
                    Integer code = jsonBody.getInteger("code");
                    if (ObjectUtil.isNotNull(code)) {
                        if (code == 0) {
                            JSONObject data = Optional.ofNullable(jsonBody.getJSONObject("data")).orElse(new JSONObject());
                            JSONArray accepted = Optional.ofNullable(data.getJSONArray("accepted")).orElse(new JSONArray());
                            JSONArray rejected = Optional.ofNullable(data.getJSONArray("rejected")).orElse(new JSONArray());
                            //有物流信息的单号
                            Set<String> trackNoWithInfo = new HashSet<>();
                            //没有物流信息的单号
                            List<String> needCancelTrackingNumbers = Lists.newArrayList();
                            // 有物流不做处理
                            for (int j = 0; j < accepted.size(); j++) {
                                JSONObject item = accepted.getJSONObject(j);
                                String number = item.getString("number");
                                JSONObject track_info = item.getJSONObject("track_info");
                                JSONObject latest_status = track_info.getJSONObject("latest_status");
                                String status = latest_status.getString("status");
                                if (status.equals("NotFound")) {

                                    needCancelTrackingNumbers.add(number);
                                } else {
                                    trackNoWithInfo.add(number);
                                }
                            }

                            if (!rejected.isEmpty()) {
                                // needCancelTrackingNumbers.add(number); todo才注册的也要先加到没物流的
                                log.info("trackinfo可能没注册的" + rejected);
                                TrackApiUtils.registerByResponds(rejected);
                            }
                            Set<Integer> orderIdsWithInfo = trackNoWithInfo.stream()
                                    .filter(trackingNumOrderIdMap::containsKey)
                                    .flatMap(trackNo -> trackingNumOrderIdMap.get(trackNo).stream())
                                    .collect(Collectors.toSet());
                            Set<Integer> orderIdsWithoutInfo = needCancelTrackingNumbers.stream()
                                    .filter(trackingNumOrderIdMap::containsKey)
                                    .flatMap(trackNo -> trackingNumOrderIdMap.get(trackNo).stream())
                                    .collect(Collectors.toSet());
                            //更新物流状态以免下次还在查
                            orderService.updatePacelstatusByOrderIds(orderIdsWithInfo, orderMap);
                            //根据订单信息取消
                            orderService.cancelNoByOrderId(orderIdsWithoutInfo, orderMap);
                        }
                    } else {
                        if (code.equals("18019909")) {
                            log.error("暂时没有跟踪信息。");
                        }
                        log.error("JOB无物流信息自动取消接口返回code" + code);
                    }
                }

            }
        }
    }

    public static void main(String[] args) {
        Gson gson = new Gson();


        Type mapType = new TypeToken<List<TrackVO>>() {
        }.getType();

        String jsonArrayStr = "[{\"number\":\"778178511740\",\"error\":{\"code\":-18019902,\"message\":\"The tracking number '778178511740' does not register, please register first.\"}},{\"number\":\"778178772108\",\"error\":{\"code\":-18019902,\"message\":\"The tracking number '778178772108' does not register, please register first.\"}},{\"number\":\"778180391168\",\"error\":{\"code\":-18019902,\"message\":\"The tracking number '778180391168' does not register, please register first.\"}},{\"number\":\"778180382440\",\"error\":{\"code\":-18019902,\"message\":\"The tracking number '778180382440' does not register, please register first.\"}},{\"number\":\"778180369876\",\"error\":{\"code\":-18019902,\"message\":\"The tracking number '778180369876' does not register, please register first.\"}},{\"number\":\"778180330486\",\"error\":{\"code\":-18019902,\"message\":\"The tracking number '778180330486' does not register, please register first.\"}}]";

        List<TrackVO> trackVOList = gson.fromJson(jsonArrayStr, mapType);
        //运单号
        List<String> numberList = trackVOList.stream().map(TrackVO::getNumber).collect(Collectors.toList());
        System.out.println(numberList);
    }

}
